export type Next = () => Promise<void|any>;
// eslint-disable-next-line no-unused-vars
type Middleware = (context: any, next: Next) => Promise<void|any>;
class Onion {
  private middlewares: Middleware[];

  constructor() {
    this.middlewares = [];
  }

  // Use a middleware
  use(middleware: Middleware) {
    this.middlewares.push(middleware);
    return this;
  }

  // Execute the middlewares
  async execute(context: any) {
    // eslint-disable-next-line no-async-promise-executor
    const dispatch = async (i: number): Promise<void> => {
      if (i < this.middlewares.length) {
        const middleware = this.middlewares[i];
        await middleware(context, async () => { await dispatch(i + 1); });
      }
    };
    await dispatch(0);
  }
}

export default Onion;

type Context = {
  pass: boolean;
};

export const sleep = (ms: number) => () => new Promise((resolve) => {
  setTimeout(resolve, ms);
});

export function begin<T extends Context>() {
  return async (context: T, n: Next) => {
    context.pass = false;
    await n();
  };
}
export function end<T extends Context>() {
  return async (context: T, n: Next) => {
    if (context.pass) await n();
    return context;
  };
}

export function success<T extends Context>() {
  return async (context: T, n: Next) => {
    context.pass = true;
    await n();
  };
}

export function wait<T extends Context>(cb: any) {
  return async (context: T, n: Next) => {
    if (typeof cb === 'function') await cb(context, n);
    await n();
  };
}
